﻿using Dapper;
using FDStudio.Core.Config;
using FDStudio.Core.Entities;
using FDStudio.Core.Entities.Enums;
using FDStudio.Core.Entities.Query;
using FDStudio.Core.Exceptions;
using FDStudio.Core.Extensions;
using FDStudio.Core.Generates;
using FDStudio.Core.Runtime;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;

namespace FDStudio.Core.Repositories
{
    /*
     CreatedOn: 2018-01-26
     CreatedBy: Jackie Lee(天宇遊龍)
     */
    /// <summary>
    /// 基于Dapper仓储上下文
    /// </summary>
    public abstract class DapperRepositoryContext : BaseContext, IDefaultRepositoryContext
    {
        /// <summary>
        /// 模型生成器
        /// </summary>
        protected IModelGenerator modelGenerator => RuntimeContext.Current.Container.Resove<IModelGenerator>();

        /// <summary>
        /// 连接字符串
        /// </summary>
        public string ConnectionString { get; private set; }

        /// <summary>
        /// 数据连接配置
        /// </summary>
        public IDbConnectionString DbConnectionString { get; private set; }

        public abstract DatabaseType DatabaseType { get; }

        /// <summary>
        /// 分页处理
        /// 如需调用PageList返回结果，此项需要处理
        /// 参数依次为：原查询语句，查询页索引（1开始），查询页大小，返回分页的语句
        /// </summary>
        public abstract Func<string, long, long, SortBy, string> PagingAction { get; }

        /// <summary>
        /// 当插入数据时，获取新插入值自增列
        /// </summary>
        public abstract string AddedIDString { get; }
        
        public DapperRepositoryContext()
        {
            Switch(DatabaseType);
        }

        /// <summary>
        /// 切换
        /// </summary>
        /// <param name="databaseType"></param>
        /// <param name="name"></param>
        public void Switch(DatabaseType databaseType, string name = "")
        {
            var connConfigs = RuntimeContext.Current.Container.ResoveAll<IDbConnectionString>();
            if (connConfigs == null || connConfigs.Count() == 0)
                throw new DbConnectionConfigException("当前上下文中不存在数据库连接类型");
            var config = connConfigs.FirstOrDefault(c => c.DatabaseType == DatabaseType && (name.IsNullOrEmpty() ? true : c.Name == name));
            if (config == null)
                throw new DbConnectionConfigException($"当前上下文中不存在{DatabaseType}类型数据库连接类型");
            ConnectionString = config.ConnectionString;
            DbConnectionString = config;
        }

        public virtual bool Add<T>(T model) where T : class, IEntity
        {
            var sql = modelGenerator.InsertString(model, out IDictionary<string, object> parameters);
            Func<IDbConnection, bool> add = conn =>
             {
                 bool res = SqlMapper.Execute(conn, sql, model) > 0;
                 if (res && !AddedIDString.IsNullOrEmpty())
                 {
                     model.Id = SqlMapper.ExecuteScalar<long>(conn, AddedIDString);
                 }
                 return res;
             };
            if (IsTranscation)
            {
                return add(_dbConnection);
            }
            using (var connection = CreateConnection())
            {
                return add(connection);
            }
        }

        public virtual bool Update<T>(T model) where T : class, IEntity
        {
            var sql = modelGenerator.UpdateString<T>(t => t.Id == t.Id);
            if (IsTranscation)
            {
                return SqlMapper.Execute(_dbConnection, sql, model) > 0;
            }
            using (var connection = CreateConnection())
            {
                return SqlMapper.Execute(connection, sql, model) > 0;
            }
        }

        public virtual bool Delete<T>(T model) where T : class, IEntity
        {
            return Delete<T>(t => t.Id == model.Id);
        }

        public virtual bool Delete<T>(Expression<Func<T, bool>> condition) where T : class, IEntity
        {
            var sql = modelGenerator.DeleteString(condition);
            if (IsTranscation)
            {
                return SqlMapper.Execute(_dbConnection, sql) > 0;
            }
            using (var connection = CreateConnection())
            {
                return SqlMapper.Execute(connection, sql) > 0;
            }
        }

        public virtual void Dispose()
        {
            DisposeTransaction(true);
        }

        public virtual void ExecuteDataReader(string sql, Action<IDataReader> read, object parameter, CommandType commandType = CommandType.Text)
        {
            using (var connection = CreateConnection())
            {
                read?.Invoke(SqlMapper.ExecuteReader(connection, sql, parameter, commandType: commandType));
            }
        }

        public virtual int ExecuteNonQuery(string sql, object parameter, CommandType commandType = CommandType.Text)
        {
            if (IsTranscation)
            {
                return SqlMapper.Execute(_dbConnection, sql, parameter, commandType: commandType);
            }
            using (var connection = CreateConnection())
            {
                return SqlMapper.Execute(connection, sql, parameter, commandType: commandType);
            }
        }

        public virtual T ExecuteScalar<T>(string sql, object parameter, CommandType commandType = CommandType.Text)
        {
            using (var connection = CreateConnection())
            {
                return SqlMapper.ExecuteScalar<T>(connection, sql, parameter, commandType: commandType);
            }
        }

        public virtual T Get<T>(long id) where T : class, IEntity
        {
            return Get<T>(t => t.Id == id);
        }

        public virtual T Get<T>(Expression<Func<T, bool>> conidtion) where T : class, IEntity
        {
            using (var connection = CreateConnection())
            {
                var sql = modelGenerator.QueryString(conidtion);
                return SqlMapper.Query<T>(connection, sql, null).FirstOrDefault();
            }
        }

        public int Count<T>(Expression<Func<T, bool>> condition) where T : class, IEntity
        {
            var sql = modelGenerator.CountString(condition);
            return ExecuteScalar<int>(sql, null);
        }

        public virtual IEnumerable<T> GetList<T>(Expression<Func<T, bool>> conidtion, IQueryOrder<T> orderBy) where T : class, IEntity
        {
            using (var connection = CreateConnection())
            {
                var sql = modelGenerator.QueryString(conidtion, orderBy);
                return SqlMapper.Query<T>(connection, sql, null);
            }
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition"></param>
        /// <param name="pagingAction"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderBy"></param>
        /// <returns></returns>
        public PagedList<T> GetList<T>(Expression<Func<T, bool>> condition, long pageIndex, long pageSize, IQueryOrder<T> orderBy) where T : class, IEntity
        {
            var sql = modelGenerator.QueryString(condition, orderBy);
            sql = PagingAction?.Invoke(sql, pageIndex, pageSize, orderBy?.KeySortBy ?? SortBy.ASC);
            using (var connection = CreateConnection())
            {
                var result = SqlMapper.Query<T>(connection, sql, null);
                sql = modelGenerator.CountString(condition);
                var count = SqlMapper.ExecuteScalar<long>(connection, sql);
                return PagedList<T>.Create(result, count, pageSize, pageIndex);
            }
        }

    }
}
